!function (global, nameSpace) {
  function plus(target, key, value) {
    Object.defineProperty(target, key, {
      value,
    });
  }

  plus(Object.prototype, 'each2', function (cb) {  //核心，each为小程序、微信网页关键字
    // 集成一个类似some的功能 ,
    // 回调函数返回 false 则函数终止,并返回 false
    // 否则函数默认返回 this(即为true)
    for (let i in this) {
      if (cb.call(this, this[i], i, this) === false) return false;
    }
    return this;
  });
  plus(Object.prototype, 'find', function (cb) {  //核心
    for (let i in this) {
      if (cb(this[i], i, this)) return this[i];
    }
    return null;
  });

// objetc
  plus(Object.prototype, 'toFreeze', function () {
    this.each2((v, i) => {
      this[i] = Object.freeze(v);
    });
    return this;
  });
  plus(Object.prototype, 'mergeBySelfKeys', function (fromObj, keysList) {
    const set = (k, v) => {
      v && (this[k] = v);
    };
    if (!keysList) {
      this.each2((v, k, o) => {
        set(k, fromObj[k]);
      });
    }
    if (Array.isArray(keysList)) {
      keysList.forEach(k => {
        set(k, fromObj[k]);
      });
    }
    return this;
  });
  plus(Object.prototype, 'clearInvalid', function () {
    return this.each2((v, k, o) => {
      v === undefined || v === '' || v === null && delete o[k];
    });
  });
  plus(Object.prototype, 'valueToNull', function () {
    this.each2((v, k, o) => o[k] = null);
    return this;
  });
  plus(Object.prototype, 'clean', function () {
    this.each2((v, k, o) => delete o[k]);
    return this;
  });
  plus(Object.prototype, 'toParams', function () {
    let rst = '';
    this.each2((v, i) => {
      rst += i + '=' + v + '&';
    });
    return rst.slice(0, -1);
  });
  plus(Object.prototype, 'toFormData', function (formName, filterKeysList) {
    // 假如 formName = info
    // 传入 {a:2,c:3,b:5}
    // 输出 {info[a]:2,info[c];3,info[b]:5}
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // 针对这种请求
    // info[mm]	    是	string	密码
    if (!formName) return this;
    const rst = {};
    if (filterKeysList) {
      this.each2((v, i) => {
        const key = filterKeysList.includes(i) ? i : `${formName}[${i}]`;
        rst[key] = encodeURIComponent(v);
      });
    } else {
      this.each2((v, i) => {
        rst[formName + '[' + i + ']'] = encodeURIComponent(v);
      });
    }
    return rst;
  });
//Number
  plus(Number.prototype, 'toTime', function () {
    //时间戳转时间的方法 例如
    //new Date(2018,0,1)  new Date(2018,3,1)  相差 90天 0小时 0 分钟0 秒
    var d = Math.floor(this / (24 * 3600 * 1000))
    //计算出小时数
    var leave1 = this % (24 * 3600 * 1000) //计算天数后剩余的毫秒数
    var h = Math.floor(leave1 / (3600 * 1000))
    //计算相差分钟数
    var leave2 = leave1 % (3600 * 1000) //计算小时数后剩余的毫秒数
    var m = Math.floor(leave2 / (60 * 1000))
    //计算相差秒数
    var leave3 = leave2 % (60 * 1000) //计算分钟数后剩余的毫秒数
    var s = Math.round(leave3 / 1000);
    // 补0
    return {d, h, m, s}.each2((v, k, o) => o[k] = ('00' + v).substr(-2));
  });
//String
  plus(String.prototype, 'each2', function (cb) {
    const len = this.length;
    for (let i = 0; i < len; i++) {
      if (cb.call(this, this[i], i, this) === false) return false;
    }
    return this;
  });
  plus(String.prototype, 'trimPhone', function () {
    return this.replace(/(\d{3})(\d{4})(\d{4})/, "$1****$3");
  });
//Math
  plus(Math, 'range', function (min, max, int) {
    const rst = min + this.random() * (max - min);
    return int ? Math.ceil(rst) : rst;
  });
//date
  plus(Date.prototype, 'format', function (fmt) {
    const o = {
      "M+": this.getMonth() + 1, //月份
      "d+": this.getDate(), //日
      "h+": this.getHours(), //小时
      "m+": this.getMinutes(), //分
      "s+": this.getSeconds(), //秒
      "q+": Math.floor((this.getMonth() + 3) / 3), //季度
      "S": this.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (let k in o)
      if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
  });
  plus(Date.prototype, 'isValid', function () {
    return this.toString() !== 'Invalid Date';
  });
//plus
  const $plus = {};
//--正则
  $plus.reg = {
    cn: /^[\u4e00-\u9fa5]+$/,
    tel: /^(\d{4}-\d{7,8}|\d{3}-\d{8})$/,
    phone: /^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[891])\d{8}$/,
    safePhone: /(\d{3})\d{4}(\d{4})/,
    ic_code: /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/,
    email: /^\w+@[a-z0-9A-Z]+\.[a-z]+$/,
    qq: /^[1-9]\d{4,12}$/,
  }
  // if (process.env.NODE_ENV === 'development') {
  //   $plus.reg.each2((v, k, o) => {
  //     o[k] = /.*/;
  //   });
  // }
  $plus.letter = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
  $plus.letter_caps = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
  //其它
  $plus.trimPhone = function (phone) {
    phone += '';
    return phone.trimPhone();
  };
  $plus.bitcode = function (str, secret_key) {
    const rst = [];
    str.each2(v => {
      rst.push(v.charCodeAt() ^ secret_key);
    });
    return String.fromCharCode(...rst);
  };
  $plus.decode_char_mess = function (data, key = 'ccf98e2c792f4fd75755eff3f2919d8a') {
    const key_len = key.length;
    const fromCharCode = String.fromCharCode;
    let rst = '';
    data.each2((v, i) => {
      let a = v.charCodeAt(), b = key[i % key_len].charCodeAt();
      rst += fromCharCode(a - b % 256);
    });
    return rst;
  };
  $plus.encode_char_mess = function (data, key = 'ccf98e2c792f4fd75755eff3f2919d8a') {
    const key_len = key.length;
    const fromCharCode = String.fromCharCode;
    let rst = '';
    data.each2((v, i) => {
      let a = v.charCodeAt(), b = key[i % key_len].charCodeAt();
      rst += fromCharCode(a + b % 256);
    });
    return rst;
  };
  $plus.reader_file = function (file) {
    return new Promise(resolve => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = function () {
        resolve(this.result);
      }
    });
  };
  $plus.download_file = function (uri, file_name) {
    const a = document.createElement('a');
    a.href = uri;
    a.download = file_name;
    a.click();
  };
  $plus.validate_form = function (form_data, form_rules) {
    return new Promise(resolve => {
      const errors = {};
      form_rules.each2((v, k) => {
        const field_value = form_data[k];
        //required
        v.required && !field_value && (errors[k] = true);
        v.pattern && !v.pattern.test(field_value) && (errors[k] = true);
      });
      resolve(Object.keys(errors).length === 0 ? null : errors);
    });
  };
  $plus.isEnableFlash = function () {
    let swf1, swf2 = navigator.plugins['Shockwave Flash'];
    try {
      swf1 = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
    } catch (e) {
    }
    return swf1 || swf2 ? true : false;
  };
  $plus.usingAutoRemLayout = function (uiWidth) {//rem布局，vue项目可以在main.js 启动
    const dom = document.documentElement;
    const resize_eName = 'orientationchange' in window ? 'orientationchange' : 'resize';
    const resize_eFn = function () {
      const clientWidth = dom.clientWidth;
      if (clientWidth >= uiWidth) {
        dom.style.fontSize = '50px';
      } else {
        dom.style.fontSize = 100 * (clientWidth / uiWidth) + 'px';
      }
    }
    dom.style.height = dom.clientHeight + 'px';//简单fix手机键盘。。 。 。
    window.addEventListener(resize_eName, resize_eFn, false);
    document.addEventListener('DOMContentLoaded', resize_eFn, false);
  };
//scroll dom
  history.scrollRestoration = 'manual'; // 允许Web应用程序在历史导航上显式地设置默认滚动恢复行为。此属性可以是自动的（auto）或者手动的（manual）。
  $plus.scroll2dom = function (dom, anim_time = 50, diff = 0) {
    if (typeof dom !== 'object') throw('');
    let timer = null;
    //获取距离浏览器顶端top
    let top_range = dom.offsetTop;
    var dom_parent = dom.offsetParent;
    while (dom_parent !== null) {//是否最外层
      top_range += dom_parent.offsetTop;
      dom_parent = dom_parent.offsetParent;
    }
    //start
    let top = window.pageYOffset;
    const speed = Math.abs(top_range - top) / anim_time;//anim_time 毫秒
    top_range += top < top_range ? -diff : diff;
    timer = setInterval(function () {
      if (top < top_range) {
        top += speed;
        if (top > top_range) {
          clearInterval(timer);
        }
      } else if (top > top_range) {
        top -= speed;
        if (top < top_range) {
          clearInterval(timer);
        }
      } else {
        clearInterval(timer);
      }
      window.scroll(0, top);
    }, 5);
  };
  //inject
  global[nameSpace] = $plus;
}(window, '$plus');
